#include "SkinAnimation.h"
#include "math/Vector3D.h"
#include "math/EulerAngles.h"
#include "math/Quaternion.h"
#include "../GameGlobal.h"
using namespace cocos2d;

bool CAnimation::LoadFromFile(const char *pFileName, CSkeletalLayer *pParent)
{

	return true;

}

void CAnimation::destroy()
{
    
}
bool CAnimation::SetLoop(const char *pAnimSetName, bool bLoop)
{
	// …Ë÷√ƒ≥∏ˆ∂Øª≠ºØµƒ—≠ª∑◊¥Ã¨

	AnimationSet *pAnimSet = _FindAnimSet(m_pAnimSet, pAnimSetName);

	if(NULL == pAnimSet)
	{
		return false;
	}

	Animation *pAnim = pAnimSet->_pAnim;

	while(NULL != pAnim)
	{
		pAnim->_bLoop = bLoop;
		pAnim = pAnim->_pNext;
	}

	return true;
}


AnimationSet *CAnimation::GetAnimSet(const char *pAnimSetName)
{
	if(NULL == pAnimSetName)
	{
		return NULL;
	}

	return _FindAnimSet(m_pAnimSet, pAnimSetName);
}

AnimationSet *CAnimation::_FindAnimSet(AnimationSet *pAnimSet, const char *pAnimName)
{
	// ’“µΩ∂Øª≠ºØ¡¥±Ì÷–µƒƒ≥∏ˆºØ£¨”√√˚◊÷¿¥ ∂±

	while(NULL != pAnimSet)
	{
		if(NULL != pAnimSet->_pName && 0 == strcmp(pAnimSet->_pName, pAnimName))
		{
			return pAnimSet;
		}

		pAnimSet = pAnimSet->_pNext;
	}

	return NULL;
}

void CAnimation::UpdateAnimSet(AnimationSet *pAnimSet, unsigned long time, bool bSmooth)
{
	_ResetFrame(m_pParentFrame);

	if(NULL != pAnimSet)
	{
		unsigned long length = pAnimSet->_length;
        Animation     *pAnim  = pAnimSet->_pAnim;
        
        while(NULL != pAnim)
        {	
            if(0 == length)
            {
                // length = 0 ÷ªªÊ÷∆µ⁄“ª÷°
                
                _UpdateAnimation(pAnim, 0, false);
            }
            else if(!pAnim->_bLoop)
            {
                // »Áπ˚Ã·»°µƒŒª÷√¥Û”⁄∂Øª≠µƒ≥§∂»£¨≤¢«“√ª”–…Ë÷√—≠ª∑£¨‘ÚÃ·»°◊Ó∫ÛµƒŒª÷√
                if (time <= length)
                {
                    _UpdateAnimation(pAnim, time, bSmooth);
                }
                else
                {
                    animationEnd(pAnimSet->_pName);
                    break;
                }
                
                
            }
            else
            {
                // »Áπ˚…Ë÷√¡À—≠ª∑ªÚ’ﬂ≥§∂»¥Û”⁄∂Øª≠µƒ≥§∂»£¨‘Ú π”√≤Â÷µ
                
                _UpdateAnimation(pAnim, time % length, bSmooth);
            }
            
            
            pAnim = pAnim->_pNext;
        }
	}

	

	Matrix4X3 mat;

	// Ω´±‰ªªæÿ’Û∏¸–¬µΩ÷°æÿ’Û÷–,’‚∏ˆæÕ «“≈¥´°£°£


	_UpdateFrame(m_pParentFrame, mat, true);
}

void CAnimation::_UpdateAnimation(Animation *pAnim, unsigned long time, bool bSmooth)
{
	// ∏¸–¬“ª∏ˆ∂Øª≠µƒ÷°
	// ∂Øª≠–≈œ¢÷–£¨ø…“‘Ã·π©¡Ω÷÷∑Ω Ωµƒ–≈œ¢
	// “ª÷÷ «÷±Ω”“‘æÿ’Ûµƒ–Œ Ω£¨¥À ±ø…“‘÷±Ω” π”√
	// ¡Ì“ª÷÷ «“‘–˝◊™+∆Ω“∆+Àı∑≈µƒ◊È∫œ∏¯≥ˆµƒ£¨
	// ¥À ±”¶∏√œ»º∆À„∏˜◊‘µƒ≤Â÷µ£¨»ª∫Û¿˚”√æÿ’Ûœ‡≥À£¨µ√µΩ◊Ó÷’µƒ±‰ªªæÿ’Û

	float rotTime = time;
	float posTime = time;

	if(pAnim->_bUsePosTime)
	{
		posTime = pAnim->_curPosTime;
	}

	if(pAnim->_bUseRotTime)
	{
		rotTime = pAnim->_curRotTime;
	}

	Frame *pFrame = pAnim->_pFrame;

	if(NULL == pFrame)
	{	
		return;
	}

	Matrix4X3 matrix;
	//D3DXMatrixIdentity(&matrix);

	Matrix4X3 matTemp;			// ¡Ÿ ±ΩË”√

	unsigned long key      = 0;
	unsigned long timeDiff = 0;			//  ±º‰≤Ó
	long timeInt  = 0;			// ≤Â÷µ ±º‰


	if(pAnim->_nRotateKeys > 0 || pAnim->_nScaleKeys > 0 || pAnim->_nPositionKeys > 0)
	{
		Vector3D    vect;	// œÚ¡ø
		Quaternion quat;	// Àƒ‘™ ˝

		if(NULL != pAnim->_pRotateKeys && pAnim->_nRotateKeys>0)
		{
			key = 0;

			// ’“µΩ‘⁄ ±º‰∑∂Œßƒ⁄µƒ“ª∏ˆº¸
			for(DWORD i=0; i<pAnim->_nRotateKeys; i++)
			{
				if(pAnim->_pRotateKeys[i]._time <= rotTime)
				{
					key = i;
				}
				else
				{
					break;
				}
			}

			if((pAnim->_nRotateKeys-1) == key || !bSmooth)
			{
				quat = pAnim->_pRotateKeys[key]._quaternion;
			}
			else
			{
				timeDiff = pAnim->_pRotateKeys[key+1]._time - pAnim->_pRotateKeys[key]._time;
				timeInt  = rotTime - pAnim->_pRotateKeys[key]._time;

				quat = Slerp(pAnim->_pRotateKeys[key]._quaternion, pAnim->_pRotateKeys[key+1]._quaternion,(float)timeInt / (float)timeDiff);
			}

			matTemp.FromQuaternion(quat);
			matrix = matrix * matTemp;

			//D3DXMatrixRotationQuaternion(&matTemp, &quat);
			//D3DXMatrixMultiply(&matrix, &matrix, &matTemp);
			//matrix = matrix * matTemp;
		}
		if(NULL != pAnim->_pScaleKeys && pAnim->_nScaleKeys>0)
		{
			key = 0;

			for(DWORD i=0; i<pAnim->_nScaleKeys; i++)
			{
				if(pAnim->_pScaleKeys[i]._time <= time)
				{
					key = i;
				}
				else
				{
					break;
				}
			}

			if((pAnim->_nScaleKeys-1) == key || !bSmooth)
			{
				vect = pAnim->_pScaleKeys[key]._scale;
			}	
			else
			{
				timeInt = time - pAnim->_pScaleKeys[key]._time;
				vect    = pAnim->_pScaleKeys[key]._scale + pAnim->_pScaleKeys[key]._scaleInterpolation * (float)timeInt;

			}

			matTemp.SetupScale(vect);
			matrix = matrix * matTemp;

			/*
			D3DXMatrixScaling(&matTemp, vect.x, vect.y, vect.z);
			D3DXMatrixMultiply(&matrix, &matrix, &matTemp);
			*/
		}

		if(NULL != pAnim->_pPositionKeys && pAnim->_nPositionKeys>0)
		{
			key = 0;

			for(DWORD i=0; i<pAnim->_nPositionKeys; i++)
			{
				if(pAnim->_pPositionKeys[i]._time <= posTime)
				{
					key = i;
				}
				else
				{
					break;
				}
			}

			if((pAnim->_nPositionKeys-1) == key || !bSmooth)
			{
				vect = pAnim->_pPositionKeys[key]._pos;
			}
			else
			{
				timeInt = posTime - pAnim->_pPositionKeys[key]._time;

				vect = pAnim->_pPositionKeys[key]._pos + pAnim->_pPositionKeys[key]._posInterpolation*(float)timeInt;
			}

			matTemp.SetupTranslation(vect);
			matrix = matrix * matTemp;
			/*
			D3DXMatrixTranslation(&matTemp, vect.x, vect.y, vect.z);
			D3DXMatrixMultiply(&matrix, &matrix, &matTemp);
			*/
		}
		pFrame->_matTransformed = matrix;
	}

	/*
	if(NULL != pAnim->_pMatrixKeys && pAnim->_nMatrixKeys > 0)
	{
		key = 0;

		for(unsigned long i=0; i<pAnim->_nMatrixKeys; i++)
		{
			if(pAnim->_pMatrixKeys[i]._time <= time)
			{
				key = i;
			}
			else
			{
				break;
			}
		}

		if((pAnim->_nMatrixKeys-1) == key || !bSmooth)
		{
			pFrame->_matTransformed = pAnim->_pMatrixKeys[key]._matrix;
		}
		else
		{
			timeInt = time - pAnim->_pMatrixKeys[key]._time ;
			matrix  = pAnim->_pMatrixKeys[key]._matInterpolation*(float)timeInt;
			pFrame->_matTransformed = matrix + pAnim->_pMatrixKeys[key]._matrix;
		}
	}
	*/

}



void CAnimation::_ResetFrame(Frame *pFrame)
{
	// Ω´À˘”–µƒ÷°÷√Œ™ø™ º÷µ

	if(NULL == pFrame)
	{
		return;
	}

	//pFrame->_matTransformed = pFrame->_matOriginal;

	pFrame->_matTransformed.Reset();
	
	if(NULL != pFrame->_pChild)
	{
		_ResetFrame(pFrame->_pChild);
	}
	if(NULL != pFrame->_pSibling)
	{
		_ResetFrame(pFrame->_pSibling);
	}
}

void CAnimation::_UpdateFrame(Frame *pFrame,const Matrix4X3 &mat, bool bTransformed)
{
	if(NULL == pFrame)
	{
		return;
	}

	if(bTransformed)
	{
		//  π”√±‰ªªæÿ’Û∏¸–¬

		pFrame->_matCombined = pFrame->_matOriginal * pFrame->_matTransformed * mat;
	}
	else
	{
		//  π”√‘≠ ºæÿ’Û∏¸–¬

		pFrame->_matCombined = pFrame->_matOriginal * mat;
	}

	if(NULL != pFrame->_pSprBone)
	{
		Vector3D vec = Vector3D(0, 0, 0) *  pFrame->_matCombined;
		pFrame->_pSprBone->setPosition(ccpAdjustRes(vec.x, vec.y));
		float p = atan2(pFrame->_matCombined.m21, pFrame->_matCombined.m22) * 180 / 3.1415926;
        

		//pFrame->_pSprBone->setPosition(ccp(pFrame->_matCombined.tx, pFrame->_matCombined.ty));

		//float p = 2*acos((matFinal.m12 - matFinal.m21)/4) * 180.0f/3.14;


		pFrame->_pSprBone->setRotation(p);
	}
	

	


	// »Áπ˚ «◊”÷°£¨‘Ú”√µ±«∞∏∏÷°µƒcombine¿¥∏¸–¬£¨
	// »Áπ˚Œ™–÷µ‹÷°£¨‘Ú”√–Œ≤Œ¥´»Îµƒæÿ’Û¿¥∏¸–¬

	_UpdateFrame(pFrame->_pChild, pFrame->_matCombined, bTransformed);

	_UpdateFrame(pFrame->_pSibling, mat, bTransformed);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
//
// CSkinAnimation
//
//////////////////////////////////////////////////////////////////////////////////////////////////

CSkeletalLayer::CSkeletalLayer(void)
: m_pCurAnimSet(NULL)
, m_startTime(0)
, m_bLoaded(false)
{
}

void CSkeletalLayer::destroy()
{
    CAnimation::destroy();
    m_pCurAnimSet = NULL;
    m_bLoaded = false;
    m_startTime = m_curTime = 0;
}

void CSkeletalLayer::onExit()
{
    destroy();
}

bool CSkeletalLayer::SetAnimation(const char *pAnimName, unsigned long startTime)
{
	if(!m_bLoaded)
	{
		return false;
	}

    if(-1 == startTime)
    {
        m_startTime = m_curTime;
    }
    else
    {
        m_startTime = startTime;
    }
	

	if(NULL == (m_pCurAnimSet =  GetAnimSet(pAnimName)))
	{
		return false;
	}
	return true;
}

void CSkeletalLayer::ResetAnimation(unsigned long time)
{
	m_startTime = time;
}

void CSkeletalLayer::UpdateAnimation(unsigned long curTime, bool bSmooth)
{
	if(m_bLoaded)
	{
        UpdateAnimSet(m_pCurAnimSet, curTime - m_startTime, bSmooth);
        animationChange();
	}
    
    m_curTime = curTime;
}
